/*
 * Copyright (C) 2012, EADS France
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.interpreter.backends.rete;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import xowl.interpreter.backends.rdf.RDFIRIReference;
import xowl.interpreter.backends.rdf.RDFNode;
import xowl.interpreter.backends.rdf.RDFTriple;

/**
 *
 * @author Laurent WOUTERS
 */
public class KnowledgeBase {
    private Map<RDFNode, Map<RDFNode, Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>>> data;
    
    private class KBIterator implements Iterator<TripleFact> {
        private RDFNode subject;
        private RDFNode property;
        private RDFNode object;
        private Map<RDFNode, Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>> sub2;
        private Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>> sub3;
        private Map<xowl.lang.owl2.Ontology, TripleFact> sub4;
        private Iterator<Map.Entry<RDFNode, Map<RDFNode, Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>>>> iter1;
        private Iterator<Map.Entry<RDFNode, Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>>> iter2;
        private Iterator<Map.Entry<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>> iter3;
        private Iterator<Map.Entry<xowl.lang.owl2.Ontology, TripleFact>> iter4;
        private TripleFact next;
        private boolean first;
        
        public KBIterator(RDFNode subject, RDFNode property, RDFNode object) {
            this.subject = subject;
            this.property = property;
            this.object = object;
            this.first = true;
            findNext();
        }
        @Override public boolean hasNext() { return (next != null); }
        @Override public TripleFact next() {
            TripleFact temp = next;
            findNext();
            return temp;
        }
        @Override public void remove() { }
        
        private void findNext() {
            next = null;
            while (iter4 == null || !iter4.hasNext())
                if (!nextSub4())
                    return;
            next = iter4.next().getValue();
        }
        private boolean nextSub4() {
            iter4 = null;
            if (object != null) {
                sub4 = null;
                while (sub4 == null) {
                    if (!nextSub3()) return false;
                    sub4 = sub3.get(object);
                }
            } else {
                while (iter3 == null || !iter3.hasNext())
                    if (!nextSub3())
                        return false;
                sub4 = iter3.next().getValue();
            }
            iter4 = sub4.entrySet().iterator();
            return true;
        }
        private boolean nextSub3() {
            iter3 = null;
            if (property != null) {
                sub3 = null;
                while (sub3 == null) {
                    if (!nextSub2()) return false;
                    sub3 = sub2.get(property);
                }
            } else {
                while (iter2 == null || !iter2.hasNext())
                    if (!nextSub2())
                        return false;
                sub3 = iter2.next().getValue();
            }
            iter3 = sub3.entrySet().iterator();
            return true;
        }
        private boolean nextSub2() {
            iter2 = null;
            if (subject != null) {
                if (!first) return false;
                first = false;
                sub2 = data.get(subject);
                if (sub2 == null) return false;
            } else {
                if (iter1 == null) iter1 = data.entrySet().iterator();
                if (!iter1.hasNext())
                    return false;
                sub2 = iter1.next().getValue();
            }
            iter2 = sub2.entrySet().iterator();
            return true;
        }
    }
    
    
    public KnowledgeBase() {
        data = new HashMap<RDFNode, Map<RDFNode, Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>>>();
    }
    
    public TripleFact getFact(RDFTriple triple, xowl.lang.owl2.Ontology ontology, boolean add) {
        Map<RDFNode, Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>> sub1 = data.get(triple.getSubject());
        Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>> sub2 = null;
        Map<xowl.lang.owl2.Ontology, TripleFact> sub3 = null;
        
        if (sub1 == null) {
            if (!add) return null;
            sub1 = new HashMap<RDFNode, Map<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>>();
            data.put(triple.getSubject(), sub1);
        }
        sub2 = sub1.get(triple.getProperty());
        if (sub2 == null) {
            if (!add) return null;
            sub2 = new HashMap<RDFNode, Map<xowl.lang.owl2.Ontology, TripleFact>>();
            sub1.put(triple.getProperty(), sub2);
        }
        sub3 = sub2.get(triple.getObject());
        if (sub3 == null) {
            if (!add) return null;
            sub3 = new HashMap<xowl.lang.owl2.Ontology, TripleFact>();
            sub2.put(triple.getObject(), sub3);
        }
        TripleFact fact = sub3.get(ontology);
        if (add) {
            if (fact == null) {
                fact = new TripleFact(triple, ontology);
                sub3.put(ontology, fact);
            }
        } else {
            if (fact == null) return null;
            sub3.remove(ontology);
            if (sub3.isEmpty())
                sub2.remove(triple.getObject());
            if (sub2.isEmpty())
                sub1.remove(triple.getProperty());
            if (sub1.isEmpty())
                data.remove(triple.getSubject());
        }
        return fact;
    }
    
    public Iterator<TripleFact> iterate(RDFNode subject, RDFNode property, RDFNode object) {
        return new KBIterator(subject, property, object);
    }
}
